Java 接入微信小程序-登录+支付完整流程实战(环境搭建+源码),网友:看完后秒懂!
如下介绍一个可运行的微信小程序登录+支付的demo。接触了小程序简易教程的,想必都知道我们必然有自己的后台应用服务器,来处理我们自己的业务逻辑、请求微信服务完成一定的功能。在此,我们的后台采用java环境,本文将首先介绍环境搭建的过程,随后介绍登录+支付的流程及代码。
一、后台web服务环境搭建
1.安装jdk、tomcat,ICP备案的域名准备。
2.配置https
https://blog.csdn.net/qq_28189091/article/details/75078164 https://blog.csdn.net/z_xuewen/article/details/78176509同时,务必将您的小程序域名绑定在小程序后端。登入小程序后台,【设置】-【开发设置】-【服务器域名】
3.部署web服务
<servlet>
<servlet-name>WechatServlet</servlet-name>
<servlet-class>com.icbc.servlet.WechatServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>WechatServlet</servlet-name>
<url-pattern>/servlet/WechatServlet</url-pattern>
</servlet-mapping>
4. log4j 应用
在开发调试中,我们免不了需要通过打印日志进行调试,因此在此增加了日志的使用。web.xml中增加配置:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classes/log4j.properties</param-value>
</context-param>
在classes增加文件,log4j.properties,内容如下:
log4j.rootLogger = INFO,toConsole,D,E
log4j.appender.toConsole=org.apache.log4j.ConsoleAppender
log4j.appender.toConsole.Target=System.out
log4j.appender.toConsole.layout=org.apache.log4j.PatternLayout
log4j.appender.toConsole.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.file = 你的目录/common.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = info
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
log4j.appender.D.DatePattern='.'yyyy-MM-dd'.log'
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.file = 你的目录/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
log4j.appender.D.DatePattern='.'yyyy-MM-dd'.log'
二、微信小程序登录+支付
1. 小程序前端目录准备
基于微信小程序工具生成的默认hello world程序,pages下先建立目录order,随后在order目录生成一个新的page,命名为order,结构如下图:
在index中增加按钮,进入order。index.wxml
<view>
<navigator class="index-intro__btn btn btn-danger btn-md" url="/pages/order/order">进入商城</navigator>
</view>
order.xml中描述商品信息,增加支付按钮 order.js 支付事件处理。
2. 登录+支付 code
流程大概分为几步:1)登录,获取code(一个code只能用一次) 2)通过code获取openid(通过请求服务器,由服务器请求微信获取并返回小程序)。微信登录+获取openid接口
2.1 登录,获取code
onLoad: function (options) {
// 登录
wx.login({
success: function (res) {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
var that = this;
if (res.code) {
console.log('获取用户登录态success!' + res.code)
app.globalData.code = res.code
} else {
console.log('获取用户登录态失败!' + res.errMsg)
}
}
})
},
2.2 通过code 获取openid(前端)
getOpenId:function(that, code){
console.log(code);
let operFlag = "getOpenid";
console.log(operFlag);
wx.request({
url: 'https://xxx/wechatserver/servlet/WechatServlet',
data: {
code:code,
operFlag:operFlag
},
header: { 'content-type': 'application/json' },
success: function (res) {
console.log(res);
var openid = res.data.openid;
console.log(openid);
that.paypay(that, openid); //预下单并支付
},
fail: function (res) {
console.log(res.data.errmsg);
console.log(res.data.errcode);
},
complete:function(res){
}
})
},
2.3 服务器端servlet(复写HttpServlet的doGet doPost函数)doPost的代码片段:
paypay: function (that, openid) {
let operFlag = 'pay';
wx.request({
url: 'https://xxx/wechatserver/servlet/WechatServlet',
data: {
openid: openid,
operFlag: operFlag
},
header: { 'content-type': 'application/json' },
success: function (res) {
console.log(res);
wx.requestPayment({
'timeStamp': res.data.timeStamp,
'nonceStr': res.data.nonceStr,
'package': res.data.package,
'signType': 'MD5',
'paySign': res.data.sign,
'success': function (res) {
if (res.errMsg == "requestPayment:ok") {
wx.showToast({
title: '支付成功'
})
}
},
'fail': function (res) {
}
})
},
fail: function (res) {
console.log(res.data.errmsg);
console.log(res.data.errcode);
},
complete: function (res) {
}
})
},
2.4 前端上送订单信息、openid请求预下单(在此,为方便,订单信息直接写死在服务器端了),若成功,则根据服务器端返回数据发起支付。关注公众号互联网架构师,在后台回复:2T,可以获取我整理和创作的 Java 系列教程非常齐全
paypay: function (that, openid) {
let operFlag = 'pay';
wx.request({
url: 'https://xxx/wechatserver/servlet/WechatServlet',
data: {
openid: openid,
operFlag: operFlag
},
header: { 'content-type': 'application/json' },
success: function (res) {
console.log(res);
wx.requestPayment({
'timeStamp': res.data.timeStamp,
'nonceStr': res.data.nonceStr,
'package': res.data.package,
'signType': 'MD5',
'paySign': res.data.sign,
'success': function (res) {
if (res.errMsg == "requestPayment:ok") {
wx.showToast({
title: '支付成功'
})
}
},
'fail': function (res) {
}
})
},
fail: function (res) {
console.log(res.data.errmsg);
console.log(res.data.errcode);
},
complete: function (res) {
}
})
},
2.5 服务器端预下单,2.6并签名返回支付请求数据
if("pay".equals(operFlag)){
String openid = request.getParameter("openid");
logger.info("openid = " + openid);
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String reqStr = getReqStr(openid); //组装预下单的请求数据
logger.info("reqStr=" + reqStr);
results = sendPost(url,reqStr);//发送post数据到微信预下单
logger.info("prepay from weixin: \n " + results);
Map<String,String> return_data = null;
try {
return_data = WXPayUtil.xmlToMap(results);//微信的一个工具类
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
logger.error(e.getMessage());
}
String return_code = return_data.get("return_code");
logger.info("return_code=" + return_code);
if("SUCCESS".equals(return_code)){
String prepay_id = return_data.get("prepay_id");
results = conPayParam(prepay_id); //组装返回数据
}else{
results ="{\"return_code\":\"fail\"}";
}
}
附函数
//组装预下单的请求数据
public static String getReqStr(String openid){
Map<String,String> data = new HashMap<String,String>();
String out_trade_no = setTradeNo();//
//
data.put("appid", appid);
data.put("mch_id",mer_id);
data.put("nonce_str", WXPayUtil.generateUUID());
data.put("sign_type", "MD5");
data.put("body", "spy test");
data.put("out_trade_no", out_trade_no);
data.put("device_info", "");
data.put("fee_type", "CNY");
data.put("total_fee", "1");//1分钱
data.put("spbill_create_ip", "123.12.12.123");
data.put("notify_url", "http://xxx/wxpay/notify");
data.put("trade_type", "JSAPI");
data.put("product_id", "12");
data.put("openid", openid);
try {
String sign = WXPayUtil.generateSignature(data, merKey, SignType.MD5);
data.put("sign", sign);
} catch (Exception e) {
e.printStackTrace();
logger.error("sign error");
}
String reqBody = null;
try {
reqBody = WXPayUtil.mapToXml(data);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return reqBody;
}
//保证唯一
public static String setTradeNo(){
String orderid = "20211909105011"+ getRandom(6);
logger.info("orderid = " + orderid);
return orderid;
}
//组装返回客户端的请求数据
public static String conPayParam(String prepayid){
logger.info("根据当前的prepayid构造返回参数= " + prepayid);
String results = "";
Map<String,String> map = new HashMap<String,String>();
map.put("appId", appid);
LocalDateTime time = LocalDateTime.now();
map.put("timeStamp", WXPayUtil.getCurrentTimestamp()+"");
map.put("nonceStr", WXPayUtil.generateUUID() );
map.put("package", "prepay_id=" + prepayid);
map.put("signType", "MD5");
String sign;
try {
sign = WXPayUtil.generateSignature(map, merKey, SignType.MD5);
map.put("sign", sign);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
logger.error(e.getMessage());
}
return JSON.toJSONString(map);
}
2.6 服务器端返回数据到微信小程序客户端后,发起微信支付,代码在2.3章节已附。2.8 服务器端收到微信的支付成功通知(省略)
三、实战中遇到的问题
1.预下单和支付请求中,签名的密钥使用的是商户密钥,但是用code获取openid是使用小程序对应的secret key,这个可以在小程序的后台看到。
2.微信小程序前端发起post请求到服务器端时,服务器端收不到请求参数。原因是:微信API接口wx.request中:a) 对于 GET 方法的数据,会将数据转换成 query string(encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)…)
b) 对于 POST 方法且 header[‘content-type’] 为 application/json 的数据,会对数据进行 JSON 序列化
所以,如果post请求,为省去服务器端反序列化的操作时,可使用header[‘content-type’] 为 application/x-www-form-urlencoded 的数据。
3.如果部署了servlet后,tomcat重启后,需要等几分钟才能生效(原因是我的机器内存比较小,而tomcat又很占用内存资源),待熟悉tomcat 调优。
作者:我是一个程序媛
来源:blog.csdn.net/proteen/article/details/80875670
5、37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...